package kb;

import kb.TBox;

import java.util.*;
import java.util.Map.Entry;

import entities.concept.Concept;
import entities.concept.NotConcept;
import entities.concept.PConcept;

import preprocessor.absorption.Absorption;
/**
 * Find out all the told subsumer and subsumee of a concept, etc.
 * @author j55wu
 *
 */
public class SimpleReasoning {
	
	TBox tb;
	HashMap<Concept, HashSet<Concept>> superClasses; // cache all told super classes
	HashMap<Concept, HashSet<Concept>> subClasses; // cache all told sub classes
	
	public SimpleReasoning(TBox tb){
		this.tb = tb; 
		this.superClasses = new HashMap<Concept, HashSet<Concept>>();
		this.subClasses = new HashMap<Concept, HashSet<Concept>>();
	}

	public HashSet<Concept> givenSuperclasses(Concept c){
		if(c.isTop()){
			this.superClasses.put(c, new HashSet<Concept>());
			return this.superClasses.get(c);
		}
		if(this.superClasses.containsKey(c)){
			return this.superClasses.get(c);
		}else{
		HashSet<Concept> superclasses = new HashSet<Concept>();
		superclasses.add(c);// of course c->c.
		Concept cc = null;
		if(c instanceof PConcept){
			if(tb.getTu().containsKey(c)){
				cc = tb.getTu().get(c);
				superclasses.add(cc);

			}			
		}else if(c.isBinarySimple()){
			if(tb.getTb().containsKey(c)){
				cc = tb.getTb().get(c);
				superclasses.add(cc);
			}
		}else if(c instanceof NotConcept){
			if(tb.getTnu().containsKey(c)){
				cc = tb.getTnu().get(c);
				superclasses.add(cc);
			}
		}
		//if the given concept is not within the absorbed axioms, try GCIs as well
		if(tb.getTg().containsKey(c)){
			cc = tb.getTg().get(c);
			superclasses.add(cc);
		}
		//recursive operations
		if(cc != null)
			superclasses.addAll(givenSuperclasses(cc));
		this.superClasses.put(c, superclasses);
		return superclasses;
		}
	}
	
	/**
	 * To do: to recursively get all the subclasses of the given concept
	 * At present should only the direct subclasses be considered for efficiency?
	 *  by J.W.
	 * @param c
	 * @return
	 */
	public HashSet<Concept> givenSubclasses(Concept c){
		if(this.subClasses.containsKey(c)){
			return this.subClasses.get(c);
		}else{
		HashSet<Concept> subclasses = new HashSet<Concept>();
		subclasses.add(c);// of course c->c.
		HashSet<Concept> recursions = new HashSet<Concept>();
		if(Absorption.CA && this.tb.getTu().values().contains(c)){
			//if c has some sub classes in Tu.
			HashSet<Concept> temp = this.getKeysFromValue(this.tb.getTu(), c);
			subclasses.addAll(temp);
			recursions.addAll(temp);
		}
		if(Absorption.BA && this.tb.getTb().values().contains(c)){
			HashSet<Concept> temp = this.getKeysFromValue(this.tb.getTb(), c);
			subclasses.addAll(temp);
			recursions.addAll(temp);
		}
		if(Absorption.NCA &&this.tb.getTnu().values().contains(c)){
			HashSet<Concept> temp = this.getKeysFromValue(this.tb.getTnu(), c);
			subclasses.addAll(temp);
			recursions.addAll(temp);
		}
		if(this.tb.getTg().values().contains(c)){
			HashSet<Concept> temp = this.getKeysFromValue(this.tb.getTg(), c);
			subclasses.addAll(temp);
			recursions.addAll(temp);
		}
		//recursive operations
		if(recursions.size()>0){
			for(Concept cc:recursions){
				subclasses.addAll(this.givenSubclasses(cc));
			}
		}
		this.subClasses.put(c, subclasses);
		return subclasses;
		}
		
	}
	
	/*
	 * Given a value, get a list/set of keys mapped to the value.
	 */
	public HashSet<Concept> getKeysFromValue(HashMap<?, ?> map, Concept value){
		HashSet<Concept> keys = new HashSet<Concept>();
		for(Entry<?, ?> o:map.entrySet()){
			if(o.getKey().equals(value)){
				if(o.getKey() instanceof Concept){
					keys.add((Concept)o.getKey());
				}else if(o.getKey() instanceof HashSet){
					for(Object oo:(HashSet<?>)o.getKey()){
						keys.add((Concept) oo);
					}
				}
				
			}
		}
		return keys;
	}
	
	
}
